﻿namespace CatClient.Message.Internals
{
    using CatClient;
    using CatClient.Message;
    using CatClient.Message.Spi;
    using CatClient.Message.Spi.Internals;
    using System;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;

    [Serializable]
    public class DefaultTaggedTransaction : AbstractMessage, ITaggedTransaction, ITransaction, IMessage
    {
        private IList<IMessage> _mChildren;
        private long _mDurationInMicro;
        private string _rootMessageId;
        private string _parentMessageId;
        private string _tag;
        private string _contextKey;

        public DefaultTaggedTransaction(string type, string name, string tag, IMessageManager messageManager) : base(type, name, messageManager)
        {
            this._tag = tag;
            this._contextKey = ((DefaultMessageManager) messageManager).CurrentContext.Key;
            this._mDurationInMicro = -1L;
            this.Standalone = false;
            IMessageTree threadLocalMessageTree = messageManager.ThreadLocalMessageTree;
            if (threadLocalMessageTree != null)
            {
                this._rootMessageId = threadLocalMessageTree.RootMessageId;
                this._parentMessageId = threadLocalMessageTree.ParentMessageId;
            }
        }

        public ITransaction AddChild(IMessage message)
        {
            if (this._mChildren == null)
            {
                this._mChildren = new List<IMessage>();
            }
            if (message != null)
            {
                this._mChildren.Add(message);
            }
            else
            {
                Cat.GetProducer().LogError(new Exception("null child message"));
            }
            return this;
        }

        public void Bind(string tag, string childMessageId, string title = null)
        {
            IEvent message = new DefaultEvent("RemoteCall", "Tagged", null);
            message.AddData(childMessageId, title ?? $"{base.Type}:{base.Name}");
            message.Timestamp = base.Timestamp;
            message.Status = "0";
            message.Complete();
            this.AddChild(message);
        }

        public bool HasChildren() => 
            ((this._mChildren != null) && (this._mChildren.Count > 0));

        public void Start()
        {
            IMessageTree threadLocalMessageTree = base.MessageManager.ThreadLocalMessageTree;
            if ((threadLocalMessageTree != null) && (threadLocalMessageTree.RootMessageId == null))
            {
                threadLocalMessageTree.ParentMessageId = this._parentMessageId;
                threadLocalMessageTree.RootMessageId = this._rootMessageId;
            }
        }

        public string ParentMessageId =>
            this._parentMessageId;

        public string RootMessageId =>
            this._rootMessageId;

        public string Tag =>
            this._tag;

        public string ContextKey =>
            this._contextKey;

        public IList<IMessage> Children =>
            (this._mChildren ?? (this._mChildren = new List<IMessage>()));

        public long DurationInMicros
        {
            get
            {
                if (this._mDurationInMicro >= 0L)
                {
                    return this._mDurationInMicro;
                }
                long num = 0L;
                int num2 = (this._mChildren == null) ? 0 : this._mChildren.Count;
                if ((num2 <= 0) || (this._mChildren == null))
                {
                    return num;
                }
                IMessage message = this._mChildren[num2 - 1];
                if (message is ITransaction)
                {
                    ITransaction transaction = message as ITransaction;
                    return (((transaction.Timestamp * 0x3e8L) + transaction.DurationInMicros) - base.TimestampInMicros);
                }
                return ((message.Timestamp * 0x3e8L) - base.TimestampInMicros);
            }
            set
            {
                this._mDurationInMicro = value;
            }
        }

        public long DurationInMillis
        {
            get
            {
                return this.DurationInMicros / 1000L;
            }
            set
            {
                this._mDurationInMicro = value * 1000L;
            }
        }

        public bool Standalone { get; set; }
    }
}

